package lru

import (
	"container/list"
)

// Cache 采用LRU策略，线程不安全
type Cache struct {
	// 最大占用空间
	maxBytes int64
	// 当前占用空间，默认值为0
	nbytes   int64
	// used队列
	ll       *list.List
	// k-v键值对存储索引
	cache    map[string]*list.Element
	// 可选的，当有缓存被淘汰后的回调函数，默认为nil
	OnEvicted func(key string, value Value)
}

type entry struct {
	key   string
	value Value
}

// Value 实现Len函数，返回占用空间
type Value interface {
	Len() int
}

// New 创建一个Cache
func New(maxBytes int64, onEvicted func(string, Value)) *Cache {
	return &Cache{
		maxBytes: maxBytes,
		ll: list.New(),
		cache: make(map[string]*list.Element),
		OnEvicted: onEvicted,
	}
}

// Get 查询key
func (c *Cache) Get(key string) (value Value, ok bool) {
	// ele 即 list.Element
	if ele, ok := c.cache[key]; ok {
		c.ll.MoveToFront(ele)
		// 将value强转到entry
		kv := ele.Value.(*entry)
		return kv.value, true
	}
	return
}

// RemoveOldest 移除最久未使用entry
func (c *Cache) RemoveOldest() {
	// 得到最久未使用entry在list中存储的形态element
	ele := c.ll.Back()
	if ele != nil {
		// 在链表中移除
		c.ll.Remove(ele)
		// 得到entry，从而得到map中的key
		kv := ele.Value.(*entry)
		delete(c.cache, kv.key)
		// 减去key、Value占用的空间
		c.nbytes -= int64(len(kv.key)) + int64(kv.value.Len())
		// 有回调函数则调用
		if c.OnEvicted != nil {
			c.OnEvicted(kv.key, kv.value)
		}
	}
}

// Add 新增entry
func (c *Cache) Add(key string, value Value) {
	// 如果key已经存在则替换，不存在则插入
	if ele, ok := c.cache[key]; ok {
		// used链表调整
		c.ll.MoveToFront(ele)
		// element强转得到entry，插入，更新nbytes
		kv := ele.Value.(*entry)
		c.nbytes += int64(value.Len()) - int64(kv.value.Len())
		// 更换value
		kv.value = value
	} else {
		// 将entry放入链表，并且得到element，更新nbytes
		ele := c.ll.PushFront(&entry{key: key, value: value})
		c.cache[key] = ele
		c.nbytes += int64(len(key)) + int64(value.Len())
	}
	// 如果设置了maxBytes并且当前超过设置值，移除最久未使用，直到不超过当前值
	for c.maxBytes != 0 && c.maxBytes < c.nbytes {
		c.RemoveOldest()
	}
}

// Len 当前数据数量
func (c *Cache) Len() int {
	return c.ll.Len()
}